import numpy as np

def IsOddNumber(i):
	'''Check if input number is odd number or not'''
	if (i-1)%2 == 0:
		return True
	else:
		return False


def GenerateMask(size, coefficient):
	'''Generate a size * size mask and filled with coefficient'''
	if not IsOddNumber(size):
		return None

	result = np.zeros((size, size), np.float64)
	result.fill(coefficient)

	return result


def Flipover(mask):
	'''Rotate mask 180 degree. This method only works for squred matrix. Rows and columns must be odd number. '''
	row, col = mask.shape
	
	if row != col:
		return None
	if not IsOddNumber(row):
		return None

	maxRowIndex = row - 1
	maxColIndex = col - 1

	halfRow = row / 2
	halfCol = col / 2
	
	result = mask.copy()
	for i in range(halfRow):
		for j in range(col):
			result[i,j], result[maxRowIndex-i, maxColIndex-j] = result[maxRowIndex-i, maxColIndex-j], result[i,j]

	i = halfRow
	for j in range(halfCol):
		result[i,j], result[i, maxColIndex-j] = result[i, maxColIndex-j], result[i,j]

	return result


def Convolve(rawImg, mask):
	maskRow, maskCol = mask.shape
	if(maskRow != maskCol) or not IsOddNumber(maskRow):
		return

	flipedMask = Flipover(mask)

	return Correlate(rawImg, flipedMask)
	

def Correlate(rawImg, mask):
	maskRow, maskCol = mask.shape
	maskRadius = (maskRow - 1) / 2

	imgHeight, imgWidth = rawImg.shape
	resultImage = rawImg.copy()

	for i in range(imgHeight):
		for j in range(imgWidth):
			tempSum = 0.0
			for xDelta in range(-maskRadius, maskRadius + 1):
				for yDelta in range(-maskRadius, maskRadius + 1):
					imgX = i + xDelta
					imgY = j + yDelta
					maskX = xDelta + maskRadius
					maskY = yDelta + maskRadius
					
					if ((imgX < 0) or (imgY < 0) or (imgX >= imgHeight) or (imgY >= imgWidth)):
						continue
					else:
						tempSum += rawImg[imgX, imgY] * mask[maskX, maskY]

			resultImage[i,j] = int(round(tempSum))

	return resultImage




if __name__ == '__main__':
	#testMatrix = np.array([range(5), range(5,10), range(10,15), range(15,20), range(20,25)])
	testMatrix = np.array([range(3), range(3,6), range(6,9)])
	print "Raw Matrix: \n"
	print testMatrix
	print "======"
	flipedMatrix = Flipover(testMatrix)
	print "Fliped Matrix: \n"
	print flipedMatrix
	print "****************************************"
	mask9 = GenerateMask(3, 1/9.0)
	print mask9
	print "======"
	mask25 = GenerateMask(5, 1/25.0)
	print mask25
	print "****************************************"
	#testImage = np.array([[8,3,4,5], [7,6,4,5], [4,5,7,8], [6,5,5,6]])
	testImage = np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
	print testImage
	print "======"
	mask1 = GenerateMask(3, 1)
	print mask9
	print "======"
	result = Convolve(testImage, mask1)
	print result
	print "****************************************"
	import cv2
	rawImg = cv2.imread('./img/fig4.jpg', cv2.CV_LOAD_IMAGE_GRAYSCALE)
	cv2.namedWindow('Original Image', cv2.CV_WINDOW_AUTOSIZE)
	cv2.imshow( "Original Image", rawImg)

	resultImage = Convolve(rawImg, mask9)
	cv2.namedWindow('Smoothed Image by 3*3', cv2.CV_WINDOW_AUTOSIZE)
	cv2.imshow( "Smoothed Image by 3*3", resultImage)

	#resultImage = Convolve(rawImg, mask25)
	#cv2.namedWindow('Smoothed Image by 5*5', cv2.CV_WINDOW_AUTOSIZE)
	#cv2.imshow( "Smoothed Image by 5*5", resultImage)

	mask0 = GenerateMask(3, 1/15.0)
	resultImage = Convolve(rawImg, mask0)
	cv2.namedWindow('Smoothed Image by 3*3 0.04', cv2.CV_WINDOW_AUTOSIZE)
	cv2.imshow( "Smoothed Image by 3*3 0.04", resultImage)

	cv2.waitKey(0)
	cv2.destroyAllWindows()
